package br.usp.icmc.biomac.client.view.impl;



import br.usp.icmc.biomac.shared.CSVData;
import br.usp.icmc.biomac.shared.CSVSerie;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;
import com.google.gwt.view.client.TreeViewModel.NodeInfo;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * The model that defines the nodes in the tree.
 */
public class PatientTreeModel implements TreeViewModel {
	
	/**
	 * A Series is an Image Series
	 */
	public static class Series implements Comparable<Series> {
	  private final String name;
	  private final CSVSerie series;
	
	  public Series(String name, CSVSerie series) {
	    this.name = name;
	    this.series = series;
	  }
	
	  public String getName() {
	    return name;
	  }
	
	  public CSVSerie getSeries() {
	    return series;
	  }

	@Override
	public int compareTo(Series arg0) {
		// TODO Auto-generated method stub
		return this.name.compareTo((String) arg0.name);
	}
	}
	
	/**
	 * A Study is a list of Series
	 */
	private static class Study implements Comparable<Study> {
	  private final String name;
	  private final CSVData data;
	  private final List<Series> seriesList = new ArrayList<Series>();
	
	  public Study(String name, CSVData data) {
	    this.name = name;
	    this.data = data;
	  }
	
	  public List<Series> addSeries(Series series) {
		seriesList.add(series);
		Collections.sort(seriesList);
	    return seriesList;
	  }
	
	  public String getName() {
	    return name;
	  }
	
	  public List<Series> getSeriesList() {
	    return seriesList;
	  }

	@Override
	public int compareTo(Study o) {
		// TODO Auto-generated method stub
		return o.name.compareTo((String) this.name);
	}
	}
	
	/**
	 * A Patient List is a list of patients
	 */
	private static class Patient implements Comparable<Patient>{
	  private final String name;
	  private final String id;
	  private final List<Study> studyLists = new ArrayList<Study>();
	
	  public Patient(String name, String id) {
	    this.name = name;
	    this.id = id;
	  }
	  
	
	  public List<Study> addStudy(Study study) {
		    studyLists.add(study);
		    Collections.sort(studyLists);
		    return studyLists;
		  }
	 
	
	  public String getName() {
	    return name;
	  }
	  
	
	  public List<Study> getStudyList() {
	    return studyLists;
	  }

	@Override
	public int compareTo(Patient o) {
		// TODO Auto-generated method stub
		return this.name.compareTo((String) o.name);
	}
	}
	
	private  static List<Patient> patients;
	private  SingleSelectionModel<Series> selectionModel = new SingleSelectionModel<Series>();

	  public PatientTreeModel(SingleSelectionModel<Series> selectionModel) {
		  
		  patients = new ArrayList<Patient>();
		  this.selectionModel = selectionModel;
	  }
		
	  
 	/**
 	 *  Create a data model based on
 	 *  the CSVData and CSVSeries lists
 	 *  
 	 */
	 public void setCsvData(List<CSVData> csvData) {
		 
		 if (csvData != null) {
			 
	 	      // add the patients, studies, and series
	 	      for (int j=0; j < csvData.size(); j++) {
	 	    	 
	 	    	 String id = csvData.get(j).getPatientID();
	 	    	 String name = csvData.get(j).getPatientName();
	 	    	 
	 	    	 if (indexOfPatient(id) < 0) {
	 	    	 	addPatient(name, id);
	 	    	 }
	 	    	 
	 	    	// find the patient
		 	    Patient patient = patients.get(indexOfPatient(csvData.get(j).getPatientID()));
	 	    	 
		 	    // create the study
	 	    	 String studyName = csvData.get(j).getStudyUID();
	 	    	 Study study = new Study(studyName, csvData.get(j));

	 	    	 patient.addStudy(study);
	 	    	 
	             // add the series
 	 	    	 List<CSVSerie> seriesList =  csvData.get(j).getSeries();
	 	    	 for (int i=0; i < seriesList.size(); i++) {
	            	 String seriesName = seriesList.get(i).getSeriesId();
	            	 Series series = new Series(seriesName, seriesList.get(i));
	            	 study.addSeries(series);
	             }
	 	      //}
		    } 
		 }
	 }
      
	 private int indexOfPatient(String id) {
			// TODO Auto-generated method stub
			  for (int i = 0; i < patients.size();  i++) {
				  if (patients.get(i).id.compareTo(id) == 0) return i;
			  }
			  return -1;
		}

	  private void addPatient(String name, String id) {
		// add a new patient to the list
		  Patient patient = new Patient(name, id);
		  patients.add(patient);
		  Collections.sort(patients);
		
	}


	/**
	   * Get the {@link NodeInfo} that provides the children of the specified
	   * value.
	   */
	  public <T> NodeInfo<?> getNodeInfo(T value) {
	    if (value == null) {
	      // LEVEL 0.
	      // We passed null as the root value. Return the composers.

	      // Create a data provider that contains the list of patients.
	      ListDataProvider<Patient> dataProvider = new ListDataProvider<PatientTreeModel.Patient>(
	          patients);

	      // Create a cell to display a patient.
	      Cell<Patient> cell = new AbstractCell<Patient>() {
	        @Override
	        public void render(Context context, Patient value, SafeHtmlBuilder sb) {
	          if (value != null) {
	        	
	            sb.appendEscaped(value.getName());
	          }
	        }
	      };
    
	      // Return a node info that pairs the data provider and the cell.
	      return new DefaultNodeInfo<Patient>(dataProvider, cell);
  
	    } else if (value instanceof Patient) {
	      // LEVEL 1.
	      // We want the studies of the patient. Return the studylist.
	    	ListDataProvider<Study> dataProvider = new ListDataProvider<PatientTreeModel.Study>(
	    	          ((Patient) value).getStudyList());
	        
	    	// create a cell to display a study
	    	Cell<Study> cell = new AbstractCell<Study>() {
	        @Override
	        public void render(Context context, Study value, SafeHtmlBuilder sb) {
	          if (value != null) {
	        	  
	            sb.appendEscaped(value.getName());
	            sb.appendHtmlConstant("<div>");
				
				Date date = value.data.getDateAcquired();
				String formattedDate = date.toString();
				sb.appendEscaped(formattedDate);
				Integer c = value.seriesList.size();
				String s = c.toString();
				sb.appendHtmlConstant(" (");
				sb.appendEscaped(s);
				sb.appendHtmlConstant(")</div>");
	          }
	        }
	      };
	      
	      return new DefaultNodeInfo<Study>(dataProvider, cell);
	      
	    } else if (value instanceof Study) {
	      // LEVEL 2 - LEAF.
	      // Return the series.
	      ListDataProvider<Series> dataProvider = new ListDataProvider<PatientTreeModel.Series>(
	    		  ((Study) value).getSeriesList());

	      // Create a cell to display a series.
	      Cell<Series> cell = new AbstractCell<Series>() {
	        @Override
	        public void render(Context context, Series value, SafeHtmlBuilder sb) {
	          if (value != null) {
	        	  
			      sb.appendHtmlConstant("<table class='imgItem'>");
			      
			      // add the image
			      sb.appendHtmlConstant("<tr><td rowspan='3'>");
			      String url = value.series.getThumbnailURL().trim();
			      sb.appendHtmlConstant("<img class='photo' src='" + url + "' />");
			      sb.appendHtmlConstant("</td>");
			       
			      // add the rotulo and info
			      sb.appendHtmlConstant("<td><h4 class='rotulo'>");
			      sb.appendEscaped(value.series.getExamType());
			      sb.appendHtmlConstant("</h4></td></tr><tr><td class='info'>");
			      sb.appendEscaped(value.series.getImagesInSeries());
			      sb.appendHtmlConstant("</td></tr></table>");
	          }
	        }
	      };  
	      //return new DefaultNodeInfo<Series>(dataProvider, cell);
	      return new DefaultNodeInfo<Series>(
	              dataProvider, cell, selectionModel,  null);
	    }
	    
	    else if (value instanceof Series) {
	    	GWT.log("value instanceof Series");
	    }
	    return null;
	  }

	  /**
	   * Check if the specified value represents a leaf node. Leaf nodes cannot be
	   * opened.
	   */
	  public boolean isLeaf(Object value) {
	    // The leaf nodes are the songs, which are Strings.
	    if (value instanceof Series) {
	      return true;
	    }
	    return false;
	  }

}
